package game;

import java.util.ArrayList;

import error.BuildingAlreadyExitsException;
import error.NotEnoughWaresException;
import buildings.*;
import units.AbstractUnit;
import units.UnitGroup;
import units.UnitSet;


public class City {
	
	//private UnitGroup units = new UnitGroup();
	private UnitSet units = new UnitSet();
	private ArrayList<AbstractBuilding> buildings = null;
	private int[] wares = {50,50,100,100,50};
	private int[] maxDepotSpace = {1000,1000,1000,1000,1000};
	private String name = "";
	private Timber_Yard timber_yard;
	private Barracks barrack;
		
	
	//Buildpoints:
	private int buildProgressPerRound = 10;
	private int buildProgressPerRoundWithoutTimberYard = 10;
	
	public City(String name) {
		buildings = new ArrayList<AbstractBuilding>();
		this.setName(name);
		Farm f = new Farm();
		f.builded();
		Lumberjack l = new Lumberjack();
		l.builded();
		Quarry q = new Quarry();
		q.builded();
		timber_yard = new Timber_Yard();
		buildings.add(f);
		buildings.add(new Fishing_Hut());
		buildings.add(l);
		buildings.add(q);
		buildings.add(new Ore_Mine());
		buildings.add(new Residential_Area());
		buildings.add(new Defence_Wall());
		buildings.add(timber_yard);
		buildings.add(new Depot());
		buildings.add(new Academy());
		barrack = new Barracks();
		buildings.add(barrack);
	}
	
	public void setName(String name) {
		this.name = name;
	}

	public String getName() {
		return name;
	}

	public int getBuildProgressPerRound() {
		if (timber_yard.isActive() && timber_yard.isBuilded()) {
			return  buildProgressPerRound;
		} else {
			return buildProgressPerRoundWithoutTimberYard;
		}
		
	}
	public void setBuildProgressPerRound(int buildProgressPerRound) {
		this.buildProgressPerRound = buildProgressPerRound;
	}
	
	public void buildOrUpgradeBuilding(AbstractBuilding b) throws BuildingAlreadyExitsException, NotEnoughWaresException {
		if (b.getBuildCountdown() != 0) {
				//TODO: Anpassen. Diese Warnung erscheint nicht wenn das Gebaeude existiert,
				//sondern wenn es grad am upgraden oder bauen ist.
				throw new BuildingAlreadyExitsException("");
			}
		if (!hasCityEnoughWaresToAffordBuilding(b)) {
			throw new NotEnoughWaresException("");
		}
		double sumCost = 0;
		int partCost;
		for (int i = 0; i<wares.length;i++) {
			partCost =  b.getCosts().getWare(b.getLevel()+1, i);
			wares[i] = wares[i] - partCost;
			sumCost = sumCost + partCost;
		}
		double ln = Math.log(b.getLevel()+1);
		sumCost = sumCost * (1+ ln*ln)*1.5;
		b.setBuildCountdown((int) sumCost);
		b.setLastFullBuildCountdown((int) sumCost);
		
	}
	
	public void increaseDepotSpace(int size) {
		for (int i =0; i < wares.length; i++) {
			maxDepotSpace[i] = maxDepotSpace[i] + size;
		}
	}
	
	public boolean hasCityEnoughWaresToAffordBuilding(AbstractBuilding b) {
		for (int i = 0; i<wares.length;i++) {
			if (wares[i] - b.getCosts().getWare(b.getLevel()+1, i) < 0)  {
				return false;
			}
		}
		return true;
	}
	
	public boolean canAffordSupply(AbstractBuilding b) {
		for (int i = 0; i<wares.length;i++) {
			if (wares[i] - b.getCosts().getSupply(b.getLevel(), i) < 0)  {
				return false;
			}
		}
		return true;
	}
	
	public void round() {
		for (AbstractBuilding a : buildings) {
			
			if (a.isBuilded()) {
				a.checkEarning(this);
				a.paySupply(this);
			}
			if (a.getBuildCountdown() > 0) {
				int tmp = a.getBuildCountdown() - getBuildProgressPerRound();
				if (tmp <= 0) {
					a.setBuildCountdown(0);
					if (a.isBuilded()) {
						a.levelup();
					} else {
						a.builded();
					}
					//Sonderbehandlung fuer bestimmte Gebaeude:
					if (a instanceof Depot) {
						increaseDepotSpace(500);
					} else if (a instanceof Timber_Yard) {
						double ln = Math.log(a.getLevel());
						setBuildProgressPerRound((int) (10 * (ln*ln+1)));
					}
				} else {
					a.setBuildCountdown(tmp);
				}
			}
		}
		
		
		if (barrack.getUnitsToBuild().size() > 0) {
			int buildpoints = barrack.getLevel()*10;
			while(buildpoints>0 && barrack.getUnitsToBuild().size()>0){
				AbstractUnit u = barrack.getUnitsToBuild().get(0);
				int uPoints = u.getBuildCountdown();
				if (uPoints !=0) {
					if (uPoints > buildpoints) {
						u.setBuildCountdown(uPoints - buildpoints);
						buildpoints = 0;
					} else {
						buildpoints = buildpoints - uPoints;
						u.setBuildCountdown(0);
						barrack.getUnitsToBuild().remove(0);
						this.units.addUnit(u);
					}
				}
			}
		}
		
	}
	
	public String waresToString() {
		return wares[0] + " Food "+ wares[1] + " Gold " + wares[2] + " Lumber " +wares[3] + " Stone " + wares[4] + " Iron.";
	}
	
	public void earn(int index, int count) {
		wares[index] =wares[index] + count;
		if (wares[index] > maxDepotSpace[index]) {
			wares[index] = maxDepotSpace[index];
		}
	}
	
	public void pay(int index, int count) {
		wares[index] =wares[index] - count;
	}

	public UnitSet getUnits() {
		return units;
	}
	
	public ArrayList<AbstractBuilding> getBuildings() {
		return buildings;
	}

	public Barracks getBarrack() {
		return barrack;
	}

	public void buildUnit(AbstractUnit u) throws NotEnoughWaresException {
		if (!hasCityEnoughWaresToAffordUnit(u)) {
			throw new NotEnoughWaresException("");
		}
		double sumCost = 0;
		int partCost;
		for (int i = 0; i<wares.length;i++) {
			partCost =  u.getCosts().getWare(1, i);
			wares[i] = wares[i] - partCost;
			sumCost = sumCost + partCost;
		}		
		barrack.getUnitsToBuild().add(u);
	}

	private boolean hasCityEnoughWaresToAffordUnit(AbstractUnit u) {
		for (int i = 0; i<wares.length;i++) {
			if (wares[i] - u.getCosts().getWare(1, i) < 0)  {
				return false;
			}
		}
		return true;
	}
	
	
	

}
